package jmine.tec.utils.cripto.key.store;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;

import jmine.tec.utils.cripto.key.PasswordManager;
import jmine.tec.utils.io.IOUtils;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;

/**
 * @author takeshi
 */
public class KeyStoreReader implements InitializingBean {

    private PasswordManager passwordManager;

    private Resource keyStoreResource;

    private final KeyStore keyStore;

    /**
     * @throws KeyStoreException e
     */
    public KeyStoreReader() throws KeyStoreException {
        this.keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    }

    /**
     * Inicializa o keyStore interno com o recurso passado, usando a senha passada no construtor
     * 
     * @param input {@link InputStream}
     * @param pwd a password a ser usada para ler o keyStore
     * @throws NoSuchAlgorithmException e
     * @throws CertificateException e
     * @throws IOException e
     */
    public void initialize(InputStream input, char[] pwd) throws NoSuchAlgorithmException, CertificateException, IOException {
        this.keyStore.load(input, pwd);
    }

    /**
     * Inicializa o keyStore interno com o recurso passado, usando a senha passada no construtor
     * 
     * @param input {@link InputStream}
     * @throws NoSuchAlgorithmException e
     * @throws CertificateException e
     * @throws IOException e
     */
    public void initialize(InputStream input) throws NoSuchAlgorithmException, CertificateException, IOException {
        this.keyStore.load(input, this.passwordManager.getPassword().toCharArray());
    }

    /**
     * Devolve a chave privada guardada com o alias passado, usando a mesma senha do keystore
     * 
     * @param privateKeyAlias String
     * @return {@link PrivateKey}
     * @throws UnrecoverableKeyException e
     * @throws KeyStoreException e
     * @throws NoSuchAlgorithmException e
     */
    public PrivateKey getPrivateKey(String privateKeyAlias) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException {
        try {
            return (PrivateKey) this.keyStore.getKey(privateKeyAlias, this.passwordManager.getPassword().toCharArray());
        } catch (IOException e) {
            throw new IllegalStateException("Cannot read password file", e);
        }
    }

    /**
     * Devolve a chave privada com o alias passado usando a senha passada
     * 
     * @param privateKeyAlias o alias da chave
     * @param keyPassword a password
     * @return {@link PrivateKey}
     * @throws UnrecoverableKeyException e
     * @throws KeyStoreException e
     * @throws NoSuchAlgorithmException e
     */
    public PrivateKey getPrivateKey(String privateKeyAlias, String keyPassword) throws UnrecoverableKeyException, KeyStoreException,
            NoSuchAlgorithmException {
        return (PrivateKey) this.keyStore.getKey(privateKeyAlias, keyPassword.toCharArray());
    }

    /**
     * Devolve a chave publica guardada no KeyStore no certificado com o nome passado
     * 
     * @param alias String
     * @return {@link PublicKey}
     * @throws KeyStoreException e
     */
    public PublicKey getPublicKey(String alias) throws KeyStoreException {
        Certificate certificate = this.keyStore.getCertificate(alias);
        if (certificate == null) {
            return null;
        }
        return certificate.getPublicKey();
    }

    /**
     * {@inheritDoc}
     */
    public void afterPropertiesSet() throws Exception {
        if (this.keyStoreResource != null) {
            InputStream input = this.keyStoreResource.getInputStream();
            try {
                this.initialize(input, this.passwordManager.getPassword().toCharArray());
            } finally {
                IOUtils.closeResourceLogException(input, KeyStoreReader.class);
            }
        }
    }

    /**
     * @return the keyStoreResource
     */
    public Resource getKeyStoreResource() {
        return keyStoreResource;
    }

    /**
     * @param keyStoreResource the keyStoreResource to set
     */
    public void setKeyStoreResource(Resource keyStoreResource) {
        this.keyStoreResource = keyStoreResource;
    }

    /**
     * @return the passwordManager
     */
    public PasswordManager getPasswordManager() {
        return passwordManager;
    }

    /**
     * @param passwordManager the passwordManager to set
     */
    public void setPasswordManager(PasswordManager passwordManager) {
        this.passwordManager = passwordManager;
    }

}
